package repo

import (
	"fmt"
	"github.com/gin-gonic/gin"
	"mine-info-server/domain/plan-manage-context/model/plan-aggregate/aggregate"
	"mine-info-server/domain/plan-manage-context/model/plan-aggregate/entity"
	"mine-info-server/infra/database/mysql/plan-manage-context/plan/convertor"
	"mine-info-server/infra/database/mysql/plan-manage-context/plan/dao"
	"mine-info-server/infra/database/mysql/plan-manage-context/plan/po"
	"mine-info-server/utils"
	"sync"
)

/*
   @author:zhongyang
   @date:2023/4/2
   @description:计划管理仓储层实现
   @tips：
	1.其实只有app层需要做一些领域操作的时候repo层才需要返回聚合根，所以很多地方的代码可能还需要修改。
*/

type PlanManageRepo struct {
	planManageConvertor convertor.PlanManageConvertor
}

func NewPlanManageRepo() *PlanManageRepo {
	return &PlanManageRepo{}
}

func (p *PlanManageRepo) CreatePlan(ctx *gin.Context, plan *aggregate.PlanAggregate) error {
	// 创建计划
	PlanPO := p.planManageConvertor.CreatePlanPO(plan)
	planDao := dao.NewPlanDao()
	err := planDao.Create(PlanPO)
	if err != nil {
		return err
	}
	// 创建计划详情
	planDetail := p.planManageConvertor.CreatePlanDetailPO(plan)
	detailDao := dao.NewPlanDetailDao()
	err = detailDao.Create(planDetail)
	if err != nil {
		return err
	}
	return nil
}

func (p *PlanManageRepo) DeleteTaskByPlanId(ctx *gin.Context, plan *aggregate.PlanAggregate) error {
	taskPO := p.planManageConvertor.DeleteTaskPO(plan)
	taskDao := dao.NewTaskDao()
	return taskDao.DeleteByPlanId(taskPO)
}

func (p *PlanManageRepo) DeleteTaskItemByPlanId(ctx *gin.Context, plan *aggregate.PlanAggregate) error {
	taskItemPO := p.planManageConvertor.DeleteTaskItemPO(plan)
	taskItemDao := dao.NewTaskItemDao()
	return taskItemDao.DeleteByPlanId(taskItemPO)
}

func (p *PlanManageRepo) DeletePlan(ctx *gin.Context, plan *aggregate.PlanAggregate) error {
	planPO := p.planManageConvertor.DeletePlanPO(plan)
	planDetailPO := p.planManageConvertor.DeletePlanDetailPO(plan)
	planReportPO := p.planManageConvertor.DeletePlanReportPO(plan)
	questionPO := p.planManageConvertor.DeleteQuestionPO(plan)
	planDao := dao.NewPlanDao()
	planDetailDao := dao.NewPlanDetailDao()
	reportDao := dao.NewPlanReportDao()
	questionDao := dao.NewQuestionDao()
	err := planDao.Delete(planPO)
	if err != nil {
		return err
	}
	err = planDetailDao.Delete(planDetailPO)
	if err != nil {
		return err
	}
	err = reportDao.DeleteByPlanId(planReportPO)
	if err != nil {
		return err
	}
	err = questionDao.DeleteByPlanId(questionPO)
	if err != nil {
		return err
	}
	return nil
}

// FindPlanById 只获取计划实体的内容
func (p *PlanManageRepo) FindPlanById(context *gin.Context, planId string) (*aggregate.PlanAggregate, error) {
	planDao := dao.NewPlanDao()
	planPO, err := planDao.GetOne(planId)
	if err != nil {
		return nil, err
	}
	planAggregate := p.planManageConvertor.CreatePlanAggregateByOption(convertor.WithPlanPO(planPO))
	return planAggregate, nil
}

func (p *PlanManageRepo) FindCompletePlanById(context *gin.Context, planId string) (*aggregate.PlanAggregate, error) {
	planDao := dao.NewPlanDao()
	planPO, err := planDao.GetOne(planId)
	if err != nil {
		return nil, err
	}
	detailDao := dao.NewPlanDetailDao()
	detailPO, err := detailDao.GetOne(planId)
	if err != nil {
		return nil, err
	}
	taskDao := dao.NewTaskDao()
	tasks, err := taskDao.FindByPlanId(planId)
	if err != nil {
		return nil, err
	}
	var wg sync.WaitGroup
	taskItemDao := dao.NewTaskItemDao()
	for _, item := range tasks {
		wg.Add(1)
		task := item
		go func() {
			defer wg.Done()
			taskItems, err := taskItemDao.FindByTaskId(task.TodoId)
			if err != nil {
				fmt.Errorf("err:%v", err)
			}
			task.TaskItemPOs = taskItems
		}()
	}
	wg.Wait()
	planAggregate := p.planManageConvertor.CreatePlanAggregateByOption(convertor.WithPlanPO(planPO), convertor.WithPlanDetailPO(detailPO), convertor.WithTaskPOs(tasks))
	return planAggregate, nil
}

// FindPlanContainsDetailById 包含计划详情的计划实体
func (p *PlanManageRepo) FindPlanContainsDetailById(context *gin.Context, planId string) (*aggregate.PlanAggregate, error) {
	planDao := dao.NewPlanDao()
	planPO, err := planDao.GetOne(planId)
	if err != nil {
		return nil, err
	}
	detailDao := dao.NewPlanDetailDao()
	detailPO, err := detailDao.GetOne(planId)
	if err != nil {
		return nil, err
	}
	planAggregate := p.planManageConvertor.CreatePlanAggregateByOption(convertor.WithPlanPO(planPO), convertor.WithPlanDetailPO(detailPO))
	return planAggregate, nil
}

func (p *PlanManageRepo) EditPlan(ctx *gin.Context, planAggregate *aggregate.PlanAggregate) error {
	planPO := p.planManageConvertor.EditPlanPO(planAggregate)
	planDao := dao.NewPlanDao()
	return planDao.Save(planPO)
}

func (p *PlanManageRepo) EditTarget(ctx *gin.Context, planAggregate *aggregate.PlanAggregate) error {
	planDetailPO := p.planManageConvertor.CreatePlanDetailPO(planAggregate)
	detailDao := dao.NewPlanDetailDao()
	return detailDao.Save(planDetailPO)
}

func (p *PlanManageRepo) SubmitPlanReport(context *gin.Context, plan *aggregate.PlanAggregate) error {
	reportPO := p.planManageConvertor.CreatePlanReportPO(plan)
	reportDao := dao.NewPlanReportDao()
	return reportDao.Save(reportPO)
}

func (p *PlanManageRepo) FindPlanContainsReportById(context *gin.Context, planId string) (*aggregate.PlanAggregate, error) {
	planDao := dao.NewPlanDao()
	planPO, err := planDao.GetOne(planId)
	if err != nil {
		return nil, err
	}
	reportDao := dao.NewPlanReportDao()
	reportPO, err := reportDao.GetOne(planId)
	if err != nil {
		return nil, err
	}
	return p.planManageConvertor.CreatePlanAggregateByOption(convertor.WithPlanPO(planPO), convertor.WithPlanReportPO(reportPO)), nil
}

func (p *PlanManageRepo) FindParentPlanListPage(context *gin.Context, page int, pageSize int, userId string) ([]*aggregate.PlanAggregate, int64, error) {
	planDao := dao.NewPlanDao()
	parentPlans, total, err := planDao.FindParentPlanListPage(page, pageSize, userId)
	if err != nil {
		return nil, 0, err
	}
	var res []*aggregate.PlanAggregate
	for _, parentPlan := range parentPlans {
		res = append(res, p.planManageConvertor.CreatePlanAggregateByOption(convertor.WithPlanPO(parentPlan)))
	}
	return res, total, nil
}

func (p *PlanManageRepo) GetTaskTotalByPlanId(planId string, finished bool) (int64, error) {
	itemDao := dao.NewTaskItemDao()
	return itemDao.GetTotalByPlanId(planId, finished)
}

func (p *PlanManageRepo) FindChildrenPlanListByParentId(parentId string, userID string) ([]*aggregate.PlanAggregate, error) {
	planDao := dao.NewPlanDao()
	childrenPlan, err := planDao.FindChildrenPlanListByParentId(parentId, userID)
	if err != nil {
		return nil, err
	}
	var res []*aggregate.PlanAggregate
	for _, childPlan := range childrenPlan {
		res = append(res, p.planManageConvertor.CreatePlanAggregateByOption(convertor.WithPlanPO(childPlan)))
	}
	return res, nil
}

func (p *PlanManageRepo) FindPlanContainsTaskByPlanIdAndDate(context *gin.Context, planId string, executeDate string) (*aggregate.PlanAggregate, error) {
	planDao := dao.NewPlanDao()
	planPO, err := planDao.GetOne(planId)
	if err != nil {
		return nil, err
	}
	taskDao := dao.NewTaskDao()
	taskPO, err := taskDao.GetOneByPlanIdAndDate(planId, executeDate)
	if err != nil {
		return nil, err
	}
	return p.planManageConvertor.CreatePlanAggregateByOption(convertor.WithPlanPO(planPO), convertor.WithTaskPO(taskPO)), nil
}

func (p *PlanManageRepo) CreateTask(context *gin.Context, plan *aggregate.PlanAggregate) error {
	// 先判断是否需要新建待办
	if plan.Tasks[0].IsNew {
		taskPO, err := p.planManageConvertor.CreateTaskPO(plan)
		if err != nil {
			return err
		}
		taskDao := dao.NewTaskDao()
		err = taskDao.Create(taskPO)
		if err != nil {
			return err
		}
	}
	// 创建待办项
	taskItemPO, err := p.planManageConvertor.CreateTaskItemPO(plan)
	if err != nil {
		return err
	}
	taskItemDao := dao.NewTaskItemDao()
	return taskItemDao.Create(taskItemPO)
}

func (p *PlanManageRepo) DeleteTaskItem(context *gin.Context, taskItemId string) error {
	itemDao := dao.NewTaskItemDao()
	return itemDao.Delete(taskItemId)
}

func (p *PlanManageRepo) DeleteTask(context *gin.Context, taskId string) error {
	// 先删除待办项
	itemDao := dao.NewTaskItemDao()
	err := itemDao.DeleteByTaskId(taskId)
	if err != nil {
		return err
	}
	taskDao := dao.NewTaskDao()
	err = taskDao.Delete(taskId)
	if err != nil {
		return err
	}
	return nil
}

func (p *PlanManageRepo) GetTaskItemById(context *gin.Context, taskItemId string) (*aggregate.PlanAggregate, error) {
	itemDao := dao.NewTaskItemDao()
	taskItemPO, err := itemDao.GetOne(taskItemId)
	if err != nil {
		return nil, err
	}
	planDao := dao.NewPlanDao()
	planPO, err := planDao.GetOne(taskItemPO.PlanId)
	taskDao := dao.NewTaskDao()
	taskPO, err := taskDao.GetOne(taskItemPO.TodoId)
	if err != nil {
		return nil, err
	}
	return p.planManageConvertor.CreatePlanAggregateByOption(convertor.WithPlanPO(planPO), convertor.WithTaskPO(taskPO), convertor.WithTaskItemPOs([]*po.TaskItemPO{taskItemPO})), nil
}

func (p *PlanManageRepo) GetTaskList(context *gin.Context, planId string, page int, pageSize int) (*aggregate.PlanAggregate, int64, error) {
	planDao := dao.NewPlanDao()
	planPO, err := planDao.GetOne(planId)
	if err != nil {
		return nil, 0, err
	}
	taskDao := dao.NewTaskDao()
	tasks, total, err := taskDao.FindPageByPlanId(utils.GetUserID(context), planId, page, pageSize)
	if err != nil {
		return nil, 0, err
	}
	var wg sync.WaitGroup
	taskItemDao := dao.NewTaskItemDao()
	for _, item := range tasks {
		wg.Add(1)
		task := item
		go func() {
			defer wg.Done()
			taskItems, err := taskItemDao.GetTaskItemListByTaskId(task.TodoId, task.PlanId)
			if err != nil {
				fmt.Errorf("err:%v", err)
			}
			task.TaskItemPOs = taskItems
		}()
	}
	wg.Wait()
	planAggregate := p.planManageConvertor.CreatePlanAggregateByOption(convertor.WithPlanPO(planPO), convertor.WithTaskPOs(tasks))
	return planAggregate, total, nil
}

func (p *PlanManageRepo) GetTaskById(context *gin.Context, taskId string) (*aggregate.PlanAggregate, error) {
	taskDao := dao.NewTaskDao()
	taskPO, err := taskDao.GetOne(taskId)
	if err != nil {
		return nil, err
	}
	planDao := dao.NewPlanDao()
	planPO, err := planDao.GetOne(taskPO.PlanId)
	if err != nil {
		return nil, err
	}
	return p.planManageConvertor.CreatePlanAggregateByOption(convertor.WithPlanPO(planPO), convertor.WithTaskPOs([]*po.TaskPO{taskPO})), nil
}

func (p *PlanManageRepo) FinishTaskItem(context *gin.Context, plan *aggregate.PlanAggregate) error {
	taskItemPO := p.planManageConvertor.FinishTaskItemPO(plan)
	itemDao := dao.NewTaskItemDao()
	return itemDao.Finish(taskItemPO)
}

func (p *PlanManageRepo) FindOneDayQuestions(context *gin.Context, planId string, taskDate string) (*aggregate.PlanAggregate, error) {
	questionDao := dao.NewQuestionDao()
	questionPOs, err := questionDao.FindOneDayQuestions(planId, taskDate, utils.GetUserID(context))
	if err != nil {
		return nil, err
	}
	return p.planManageConvertor.CreatePlanAggregateByOption(convertor.WithQuestionPOs(questionPOs)), nil
}

func (p *PlanManageRepo) SearchQuestions(context *gin.Context, planId string, content string) (*aggregate.PlanAggregate, error) {
	questionDao := dao.NewQuestionDao()
	questionPOs, err := questionDao.SearchQuestions(utils.GetUserID(context), planId, content)
	if err != nil {
		return nil, err
	}
	return p.planManageConvertor.CreatePlanAggregateByOption(convertor.WithQuestionPOs(questionPOs)), nil
}

func (p *PlanManageRepo) IsFinishTask(context *gin.Context, planID string) (bool, error) {
	planDao := dao.NewPlanDao()
	return planDao.IsFinishAllTasks(planID)
}

func (p *PlanManageRepo) GetOngoingTasksItem(context *gin.Context, scope string) ([]*entity.TaskItem, error) {
	itemDao := dao.NewTaskItemDao()
	itemDao.GetOngoingTaskItems(scope)
	return nil, nil
}

func (p *PlanManageRepo) FindOngoingPlanIds(userId string) ([]string, error) {
	planDao := dao.NewPlanDao()
	return planDao.FindOngoingPlanIds(userId)
}

func (p *PlanManageRepo) GetOngoingTasksByPlanIds(context *gin.Context, planIds []string, scope string) ([]*entity.Task, error) {
	taskDao := dao.NewTaskDao()
	taskPOs, err := taskDao.FindOngoingTasksByPlanIds(planIds, scope)
	if err != nil {
		return nil, err
	}
	return p.planManageConvertor.ConvertPO2TaskEntities(taskPOs), nil
}

func (p *PlanManageRepo) FindOngoingTaskItems(todoId string) ([]*entity.TaskItem, error) {
	itemDao := dao.NewTaskItemDao()
	itemPOs, err := itemDao.FindOngoing(todoId)
	if err != nil {
		return nil, err
	}
	return p.planManageConvertor.ConvertPO2TaskItemEntities(itemPOs), nil
}

func (p *PlanManageRepo) EditTaskItem(plan *aggregate.PlanAggregate) error {
	taskPOs := p.planManageConvertor.EditTaskAndItemPO(plan)
	taskDao := dao.NewTaskDao()
	// 这里需要测试一下，如果是原本就有的，那会不会新增一个待办
	err := taskDao.Save(taskPOs[0])
	if err != nil {
		return err
	}
	itemDao := dao.NewTaskItemDao()
	return itemDao.Edit(taskPOs[0].TaskItemPOs[0])
}

func (p *PlanManageRepo) BatchDeleteQuestions(deleteQuestions []*entity.Question) error {
	questionsPOs := p.planManageConvertor.ConvertQuestionPO(deleteQuestions)
	questionDao := dao.NewQuestionDao()
	return questionDao.BatchDelete(questionsPOs)
}

func (p *PlanManageRepo) SaveQuestions(plan *aggregate.PlanAggregate) error {
	questionsPOs := p.planManageConvertor.ConvertQuestionPO(plan.Questions)
	questionDao := dao.NewQuestionDao()
	return questionDao.Save(questionsPOs)
}

func (p *PlanManageRepo) FinishPlan(plan *aggregate.PlanAggregate) error {
	planPO := p.planManageConvertor.FinishPlanPO(plan)
	planDao := dao.NewPlanDao()
	return planDao.Finish(planPO)
}

func (p *PlanManageRepo) FindTaskByPlanIdAndDate(context *gin.Context, planId string, executeDate string) ([]*entity.Task, error) {
	taskDao := dao.NewTaskDao()
	taskPOs, err := taskDao.FindByPlanIdAndDate(planId, executeDate)
	if err != nil {
		return nil, err
	}
	return p.planManageConvertor.ConvertPO2TaskEntities(taskPOs), nil
}

func (p *PlanManageRepo) FindAllPlanByUserId(userId string) ([]*aggregate.PlanAggregate, error) {
	planDao := dao.NewPlanDao()
	planPOs, err := planDao.FindAllByUserId(userId)
	if err != nil {
		return nil, err
	}
	var res []*aggregate.PlanAggregate
	for _, planPO := range planPOs {
		planAggregate := p.planManageConvertor.CreatePlanAggregateByOption(convertor.WithPlanPO(planPO))
		res = append(res, planAggregate)
	}
	return res, nil
}
